#pragma once
#include <iostream>
#include <google/protobuf/map.h>
#include <vector>
#include <string>
#include "../mqcommon/Helper.hpp"
#include "../mqcommon/Log.hpp"
#include "../mqcommon/msg.pb.h"
using namespace std;
namespace mq
{
    class Exchange
    {
    public:
        using ExchangePtr = shared_ptr<Exchange>;
        string _name;
        mq::ExchangeType _type;
        bool _durable;
        bool _auto_delete;
        google::protobuf::Map<string, string> _args;

    public:
        Exchange() {}
        Exchange(const string &name, const mq::ExchangeType &type, bool durable, bool auto_delete, const google::protobuf::Map<string, string> &args)
            : _name(name), _type(type), _durable(durable), _auto_delete(auto_delete), _args(args)
        {
        }
        void setArgs(const string &str)
        {
            vector<string> arry;
            mq::StringHelper::splite(str, "&", arry);
            for (auto &s : arry)
            {
                size_t pos = s.find("=");
                if (pos == string::npos)
                {
                    DLOG("字符串解析出错");
                    continue;
                }
                string key = s.substr(0, pos);
                string val = s.substr(pos + 1);
                _args[key] = val;
            }
        }
        string getArgs()
        {
            string ret;
            for (auto &it : _args)
            {
                ret += (it.first + "=" + it.second + "&");
            }
            return ret;
        }
    };
    class ExchangeMapper
    {
        using ExchangeMap = std::unordered_map<std::string, Exchange::ExchangePtr>;

    public:
        ExchangeMapper(const string &dbfile) : _sql_helper(dbfile)
        {
            string dir = mq::FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(dir);
            assert(_sql_helper.open());
            createTable();
        }
        ~ExchangeMapper()
        {
            _sql_helper.close();
        }
        // 创建交换机表
        void createTable()
        {
            string sql = "create table if not exists exchange_table\
            (name vrachar(32) primary key,\
            type int,\
            durable int, \
            auto_delete int, \
            args varchar(128));";
            if (_sql_helper.exec(sql.c_str(), nullptr, nullptr) == false)
            {
                DLOG("数据库表创建失败");
                abort();
            }
        }
        // 删除交换机表
        void removeTable()
        {
            string sql = "drop table exchange_table";
            if (_sql_helper.exec(sql.c_str(), nullptr, nullptr) == false)
            {
                DLOG("数据库表删除失败");
                abort();
            }
        }
        // 向交换机表中插入交换机
        bool insert(Exchange::ExchangePtr &exp)
        {
            stringstream ss;
            ss << "insert into exchange_table values(";
            ss << "'" << exp->_name << "',";
            ss << exp->_type << ",";
            ss << exp->_durable << ",";
            ss << exp->_auto_delete << ",";
            ss << "'" << exp->getArgs() << "');";
            return _sql_helper.exec(ss.str(), nullptr, nullptr);
        }
        // 从交换机表中移除交换机
        void remove(const string &name)
        {
            stringstream ss;
            ss << "delete from exchange_table where name=";
            ss << "'" << name << "';";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }
        // 从持久化文件恢复
        ExchangeMap recovery()
        {
            ExchangeMap result;
            stringstream ss;
            ss << "select name, type, durable, auto_delete, args from exchange_table;";
            if (_sql_helper.exec(ss.str(), ExchangeMapper::seletct_callback, &result) == false)
            {
                DLOG("数据库表查询失败");
                abort();
            }
            return result;
        }

    private:
        static int seletct_callback(void *args, int count, char **row, char **fields)
        {
            ExchangeMap *result = static_cast<ExchangeMap *>(args);
            auto exp = make_shared<mq::Exchange>();
            exp->_name = row[0];
            exp->_type = (mq::ExchangeType)stoi(row[1]);
            exp->_durable = (bool)stoi(row[2]);
            exp->_auto_delete = (bool)stoi(row[3]);
            if (row[4])
                exp->setArgs(row[4]);
            result->insert(make_pair(exp->_name, exp));
            return 0;
        }

    private:
        mq::SqliteHelper _sql_helper;
    };
    class ExchangeManager
    {

    public:
        using ExchangeMap = unordered_map<string, Exchange::ExchangePtr>;
        using ExchangeManagerPtr = shared_ptr<ExchangeManager>;
        ExchangeManager(const string &dbfile) : _mappers(dbfile)
        {
            _exchanges = _mappers.recovery();
        }
        // 新建一个交换机并添加到交换机管理队列中
        bool declareExchange(const std::string &name, ExchangeType type, bool durable, bool auto_delete,
                             const google::protobuf::Map<string, string> &args)
        {
            unique_lock<mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it != _exchanges.end())
                return true;
            auto exp = make_shared<mq::Exchange>();
            exp->_name = name;
            exp->_type = type;
            exp->_durable = durable;
            exp->_auto_delete = auto_delete;
            exp->_args = args;
            if (durable == true)
            {
                if (_mappers.insert(exp) == false)
                {
                    DLOG("交换机声明/创建失败");
                    return false;
                }
            }
            _exchanges.insert(make_pair(name, exp));
            return true;
        }
        // 从交换机管理队列中删除特定交换机
        void deleteExchange(const std::string &name)
        {
            unique_lock<mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return;
            if (it->second->_durable == true)
                _mappers.remove(name);
            _exchanges.erase(name);
        }

        // 从交换机管理队列中查找特定交换机
        Exchange::ExchangePtr selectExchange(const std::string &name)
        {
            unique_lock<mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return Exchange::ExchangePtr();
            return it->second;
        }
        // 判断交换机管理队列中特定交换机是否存在
        bool exists(const std::string &name)
        {
            unique_lock<mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return false;
            return true;
        }
        // 获取交换机管理队列中交换机数量
        size_t size()
        {
            unique_lock<mutex> lock(_mutex);
            return _exchanges.size();
        }
        // 清空交换机管理队列
        void clear()
        {
            unique_lock<mutex> lock(_mutex);
            _mappers.removeTable();
            _exchanges.clear();
        }

    private:
        mutex _mutex;
        ExchangeMap _exchanges;  // 交换机管理队列
        ExchangeMapper _mappers; // 可持久化操作句柄
    };
};